Mybatis拦截器与PageHelper一起使用无法拦截分页前的sql解决方案 您所在的位置:网站首页 mybatis pagehelper不生效 Mybatis拦截器与PageHelper一起使用无法拦截分页前的sql解决方案

Mybatis拦截器与PageHelper一起使用无法拦截分页前的sql解决方案

#Mybatis拦截器与PageHelper一起使用无法拦截分页前的sql解决方案| 来源: 网络整理| 查看: 265

在使用mybatis拦截器的时候,遇到一个很蛋疼的问题,就是无法拦截pagehelper分页插件执行之前的sql,每次拦截都是已经拼接完sql,而且无法拦截pagehelper中那个select count语句,然而今天我来给大家讲下我是如何解决这个问题的

我在网上查过N篇文章,结果都是直接给出了pagehelper的官方文档,一开始看到别人写的时候还以为这个人这么6的,结果这是官方文档~,这里我也不多说了,先将官网放出来你们有空可以看看

https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/Interceptor.md

通过官方描述我们不难发现,其实我们无法获取分页之前的sql是因为他比我们早一步拦截,所以不管我们怎么获取都是被处理完的sql

所以我们去查看他的源码,通过源码查看不难发现,其实他们主要使用PageInterceptor进行拦截

@Intercepts( { @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), } ) public class PageInterceptor implements Interceptor {

由上述代码我们可以看到,他是拦截了Executor这个类的query方法,并且拦截了4个参数或者6个参数,具体参看上面官方文档

由于mybatis拦截器的顺序是先拦截4个参数的方法,然后在到6个参数的方法,所以官方文档有这么一个描述

在这里插入图片描述

所以我们就得直接拦截四个参数的方法,然后再跳到PageInterceptor,这样我们就能先拿到sql了,所以我们来尝试一下

@Intercepts( { @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), } ) @Component public class PowerInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; RowBounds rowBounds = (RowBounds) args[2]; ResultHandler resultHandler = (ResultHandler) args[3]; Executor executor = (Executor) invocation.getTarget(); CacheKey cacheKey; BoundSql boundSql; //由于逻辑关系,只会进入一次 if(args.length == 4){ //4 个参数时 boundSql = ms.getBoundSql(parameter); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } else { //6 个参数时 cacheKey = (CacheKey) args[4]; boundSql = (BoundSql) args[5]; } //TODO 自己要进行的各种处理 //注:下面的方法可以根据自己的逻辑调用多次,在分页插件中,count 和 page 各调用了一次 return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }

首先我建了一个类叫PowerInterceptor,里面的实现方法和文档中的QueryInterceptor中的代码一样,但是我们拦截了4个参数的方法,然后测试发现,还是不得,他是先执行QueryInterceptor>PageInterceptor,然后这个PowerInterceptor的拦截直接跳过了?因为QueryInterceptor直接执行了下面这句话,导致他不会再次进入四个参数的拦截,然后只能拦截六个参数的方法?

executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);

然后我再看了一下文档,喔,原来是要配置xml的,好,我配!

在这里插入图片描述

毕竟我们spring boot用惯了就很少用过xml来配置了,所以今天来写下吧

在application.properties或者application.yml文件中加入,下面代码为properties文件

mybatis.config-location=classpath:mybatis-config.xml

然后在resource目录新建mybatis-config.xml,添加以下内容

DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

看到这,是不是觉得只要我们PowerInterceptor优先于QueryInterceptor就行了,至于为什么配置是在QueryInterceptor下一行,还是那句话,先看文档。。。

其实我一开始也是以为这就行了,结果mmp,什么都没改变,还是先执行QueryInterceptor>PageInterceptor,PageInterceptor不执行,还是只有拦截六个参数的方法的时候才能拦截,顺序还是在PageInterceptor后

emmm,这鬼东西太烦人了,然后经过一番思考,那我能不能将PageInterceptor也配上,让他在PowerInterceptor之后执行,好吧既然想了就来试试

感觉思路很正常,没错这是个要大成的征兆,是吧

在这里插入图片描述

结果,就是我们程序员的日常,“感觉”自己的思路完全可行,然后出现以下代码

Caused by: java.lang.RuntimeException: 在系统中发现了多个分页插件,请检查系统配置!

我淦!

意思是PageInterceptor被配置了,如果我们再配置就变多个了

此处1w个草泥马奔腾而过,这该如何是好

最终我做了一个决定,将pagehelper抽出来放在自己的程序上配置,而不是在依赖中,我们先把pagehelper的依赖去掉,然后点击下面链接

在github上拿到pagehelper源码,然后将pagehelper全部源码迁进自己项目的包下

在这里插入图片描述

然后添加部分依赖

com.github.jsqlparser jsqlparser 3.2 com.google.guava guava 19.0 compile true

mybatis-config.xml的配置不变,然后断点测试

我曹,好像行了

然后修改一下PowerInterceptor的拦截。在args.length == 4也就是拦截四个方法的时候修改sql,ReflectUtil在另一章节已给出,

现在测试一条数据拦截

@Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; RowBounds rowBounds = (RowBounds) args[2]; ResultHandler resultHandler = (ResultHandler) args[3]; Executor executor = (Executor) invocation.getTarget(); CacheKey cacheKey; BoundSql boundSql; //由于逻辑关系,只会进入一次 if(args.length == 4){ //4 个参数时 boundSql = ms.getBoundSql(parameter); //测试拦截 String sql=boundSql.getSql(); sql="select * from ("+sql + ") as p where p.Id = '22c66bebfa2445aa99965df7e3e5b318'"; ReflectUtil.setFieldValue(boundSql, "sql", sql); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } else { //6 个参数时 cacheKey = (CacheKey) args[4]; boundSql = (BoundSql) args[5]; } //TODO 自己要进行的各种处理 //注:下面的方法可以根据自己的逻辑调用多次,在分页插件中,count 和 page 各调用了一次 return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql); }

然后测试结果

在这里插入图片描述

到这说明已经拦截成功了,在分页之前修改了sql,然后通过分页插件执行了select count和limit

但是这个方法有点笨,毕竟要将pagehelper源码拉过来,虽然不是很多,其实如果能解决那个PageInterceptor加载就不用将代码拉过来了,如果有人可以用其他办法解决请私聊我,我也想知道

解决方法

在启动类上加上

@SpringBootApplication(exclude = {PageHelperAutoConfiguration.class})

ps:评论的小伙伴告知可以使用这个方法,暂无时间测试,各位可以自行测试

ReflectUtil工具类和数据权限拦截的具体实现请点击下面链接

springboot mybatis拦截器实现数据权限控制



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有